﻿
//import('https://docs.opencv.org/4.10.0/opencv.js')
//console.log = ((oldFunc) => function () { oldFunc.apply(this, arguments) })(console.log)
//查找登录

let uinfo = null
window.onload = async () => {
    await selectLogin("Aa13242463318","chenjing8/")
}
async function selectLogin(lname, pwd) {
    let delay=2000
    await retry(() => {
        let le = Array.from(document.querySelectorAll(".login_detail li.gd-tabs-nav > span")).filter(p => p.textContent.includes('个人登录'))[0]
        if (!le) throw Error("未找到:个人登录")
            le.click()
    }, 3, delay)
    await retry(() => {
        let el = document.querySelector("div.account_verifying.gd-input.gd-input-text > input[placeholder=请输入图中算式的计算结果]")
        if (el) return el;
        let loginBtn = Array.from(document.querySelectorAll("a.login-tab")).filter(p => p.checkVisibility() && p.textContent.includes('账号密码'))[0]
        if (loginBtn)
            loginBtn.click()
        throw Error("未找到:请输入图中算式的计算结果")
    }, 3, delay)

    document.querySelector("div.gd-input.gd-input-text > input[placeholder=请输入账号]").value = lname
    document.querySelector("div.gd-input.gd-input-password > input[placeholder=请输入密码]").value = pwd
    let checkImg=document.querySelector("div.captcha-body.captcha[title=点击刷新] > img")
    document.querySelector("form > div.login-btn button > span")
}
//
console.log("add load")
waitLoadCV()
var gcfg = { uinfo: {}, isStop: 0, runTime: new Date(), lastReqTime:new Date()}
gcfg.runTime.setHours(9, 0, 0, 0)
gcfg.imgInfo=-1
async function runByTime() {
    if (gcfg.isStop)
        return console.log('停止记时！', gcfg.isStop)
    await bgInfoGetRun()
    let runTime = gcfg.runTime 
    let nowDate = new Date()
    let msX = nowDate - runTime
    console.log("定时差：", msX)
    if (msX < -3000) {
        if (nowDate - gcfg.lastReqTime > 110000) //保持session,两分钟刷新下背景数据
            bgInfoGet()
        return setTimeout(runByTime, 2000)
    }
    else if (msX < -200) {
        if (gcfg.imgInfo===-1)
            imgReq()
        return setTimeout(runByTime, 100)
    }
    while (new Date() - runTime < -30) { }
    console.log("计时最后差：", new Date() - runTime)
    retry(run,3,-1)
}
async function run() {
    console.log('开始执行run')
    await bgInfoGetRun()
    let uinfoTmp = gcfg.uinfo
    
    let imgInfo = gcfg.imgInfo
    if (imgInfo===-1||!imgInfo)
        await retry(imgReq, 3, -1)
    if (!imgInfo)
        return console.log('图片获取异常', imgInfo)
    console.log("获取图片完成，开始处理图片")
    let img2 = await loadImageBy64(imgInfo.data.slidingImage)
    let img1 = await loadImageBy64(imgInfo.data.originalImage)
    let rePoint = handleImage(img1, img2)
    console.log("图片处理完成！开始验证图片")
    let reCheck = await checkReq(rePoint.x, imgInfo.data.id, uinfoTmp.orderPlanId)
    if (!reCheck) {
        console.log('验证图片异常:', reCheck, rePoint)
        throw Error("图片验证码返回验证失败" + reCheck)
    }
    console.log("验证图片完成！开始发送验证码")
    let reSms = await retry(BgSendSms_gd, 2, -1)
    if (!reSms) {
        throw Error("发送验证码异常",reSms)
    }
    console.log("发送验证码完成！等待输入短信验证码")
    let smsCode = prompt(uinfoTmp.phone + "验证码输入：")
    if (smsCode)
        await submit2_gd(smsCode)
    console.log('执行完成')
}
async function bgInfoGetRun() {
    if (!gcfg.uinfo.orderPlanId)
        await bgInfoGet()
}
async function bgInfoGet() {
    let url = `https://cyry.jtj.gz.gov.cn:5531/uas/app/login/person/backgroundInfo?_=${new Date().valueOf()}`
    let text = await fetch(url).then(p => p.text())
    gcfg.lastReqTime = new Date() //更新最后会话时间
    let orderPlanId = /;\/\/"([0-9a-fA-F]{32})"/.exec(text)?.[1] || ''
    let phone = /手机号码：([0-9]{11})/.exec(text)?.[1] || ''
    if (orderPlanId) {    
        gcfg.uinfo.orderPlanId = orderPlanId
        gcfg.uinfo.phone = phone
        console.log('backgroundInfo获取的信息：', gcfg.uinfo)
        return gcfg.uinfo
    }
    return null
}
/** 1滑动验证码获取
 * @example 
    {
    "code": 0,
    "data": {
        "id": "f19dbe50db2544ab9006dc3ed95eaf46",
        "originalImage": "",
        "slidingImage": "",
        "yheight": 18,
        "xwidth": 0
    },
    "mobile": "15678913285",
    "message": "成功"
    }
 */
function imgReq() {
    return axios.get("https://cyry.jtj.gz.gov.cn:5531/uas/app/background/captcha/generate?=_" + new Date().valueOf(), { timeout :9000})
        .then(p => p.data)
        .then(p => {   
            console.log(p, "图片")
            if (p.code !== 0)
                return console.log("返回异常", p)
            gcfg.imgInfo=p
            return p
        })
}

/** 2滑动验证提交
 * @param {Number} xPos 位置
 * @param {any} captchaId
 */
function checkReq(xPos, captchaId, orderPlanId = '2efdc19af9ea4407898fcd69e0b42086') {
    return fetch("https://cyry.jtj.gz.gov.cn:5531/uas/app/background/captcha/check",
        {
            "body": JSON.stringify({ xPos, captchaId, orderPlanId }),
            "method": "POST",
            "headers": {"content-type": "application/json"}
        })
        .then(p => p.json())
        .then(p => { 
            if (p.code !== 0)
                return console.log("checkReq返回异常", p)
            return p
        })
}
/** 验证码发送 */
function BgSendSms_gd() {
    return fetch("https://cyry.jtj.gz.gov.cn:5531/uas/app/background/captcha/BgSendSms?_=" + new Date().valueOf())
        .then(p => p.json())
        .then(p => {
            if (p.code !== 0)
                return console.log(p, "发送验证码返回结果")
            return p
        })
}
/** 验证码提交 */
function submit2_gd(smsCode) {
    return fetch(`https://cyry.jtj.gz.gov.cn:5531/uas/app/login/person/background/submit2?smsCode=${smsCode}`,
        {
            "method": "POST",
            "headers": { "content-type": "application/json" }
        })
        .then(p => p.json()) //code=0成功
        .then(p => {
            console.log(p)
            return p
        })
}

/** 通信  <script src="https://unpkg.com/@supabase/supabase-js@2"></script> 
 * https://s4.zstatic.net/ajax/libs/axios/1.7.8/axios.min.js   
 * https://cdn.jsdelivr.net/npm/@supabase/supabase-js/dist/umd/supabase.min.js
 * */
var chaa=null
async function sup() {
  await import('https://unpkg.com/@supabase/supabase-js@2.49.1/dist/umd/supabase.js')
    var supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxobXpmbWhrYXp4cGVjY21zb3B3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDA5NjYxMTQsImV4cCI6MjA1NjU0MjExNH0.k2omCIjv7tpUuVgYNXWmYdqHsIjNod1CLNBOxouat3I'
    var supabase1 = supabase.createClient('https://lhmzfmhkazxpeccmsopw.supabase.co', supabaseKey)
    let tbName ='kvinfo'
    const { data, error } = await supabase1
        .from(tbName)
        .select().order('id', { ascending: false }).limit(10)
    console.log(data)
    chaa = supabase1
        .channel('dataSub') // 频道名称需唯一
        .on(
            'postgres_changes',
            {
                event: 'INSERT',       // 监听插入操作
                schema: 'public',      // 数据库schema名称
                table: tbName      // 目标数据表
            },
            (payload) => {
                console.log('新增消息内容:', payload)
                // 在此处更新UI或处理业务逻辑
            }
        )
        .subscribe()
    chaa.send({ type: 'heartbeat' })
     await supabase1
        .from(tbName)
        .insert({ key: '12369', val: 'abc' })
}
function supaSelect() {

    
}
function addSupa(data) {

    //
    fetch("https://lhmzfmhkazxpeccmsopw.supabase.co/rest/v1/kvinfo", {
        "headers": {
            "apikey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxobXpmbWhrYXp4cGVjY21zb3B3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDA5NjYxMTQsImV4cCI6MjA1NjU0MjExNH0.k2omCIjv7tpUuVgYNXWmYdqHsIjNod1CLNBOxouat3I",
            "content-type": "application/json"
        },
        "body": data, //"{\"key\":\"12369bbbbb\",\"val\":\"abcbbbbb\"}",
        "method": "POST"
    
    });
}
function supaAxios() {
    let tmp = axios.create({
        baseUrl: "https://lhmzfmhkazxpeccmsopw.supabase.co/rest/v1/",
        "headers": {
            "apikey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxobXpmbWhrYXp4cGVjY21zb3B3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDA5NjYxMTQsImV4cCI6MjA1NjU0MjExNH0.k2omCIjv7tpUuVgYNXWmYdqHsIjNod1CLNBOxouat3I"
        }
    })
    return tmp
}
function selectSupa(dto) {
    supaAxios().get('data?select=*&order=id.desc&limit=10')
    .then(p => p.data())
        .then(p => console.log(p))
}

function jsAdd() {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2';
    script.onload = () => { console.log('加载完成') };
    document.head.appendChild(script);
}



/** vc   <script async src="https://docs.opencv.org/4.10.0/opencv.js" onload="waitLoadCV()"></script> */
var isOpenCVReady = null
// 初始化OpenCV
function waitLoadCV() {
    if (typeof (cv) == "undefined")
        setTimeout(waitLoadCV, 500)
    else {
        cv['onRuntimeInitialized'] = () => {
            isOpenCVReady = true;
            console.log("OpenCV 就绪");
        }
        isOpenCVReady = true;
        console.log("OpenCV 就绪2")
    }
}

function mrun() {
    let img1 = stmpl.files[0]
    let img2 = stmpl.files[0]
    handleImage(img1, img2)
}
// 图像预处理流水线
function preprocessImage(src) {
    const gray = new cv.Mat();
    cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);

    // 边缘检测强化轮廓
    const edges = new cv.Mat();
    cv.Canny(gray, edges, 150, 200); // 调整阈值优化边缘检测

    // 形态学操作增强连续轮廓
    const kernel = cv.getStructuringElement(cv.MORPH_RECT, new cv.Size(3, 3));
    cv.dilate(edges, edges, kernel);

    gray.delete();
    return edges;
}

// 执行模板匹配
function matchTemplate(sceneEdges, templateEdges) {
    const result = new cv.Mat();
    const matchMethod = cv.TM_CCOEFF_NORMED; // 归一化相关系数法

    // 执行边缘图匹配
    cv.matchTemplate(sceneEdges, templateEdges, result, matchMethod);

    // 获取最佳匹配位置
    const minMax = cv.minMaxLoc(result);
    console.log(`最大匹配值: ${minMax.maxVal.toFixed(2)} 位置: (${minMax.maxLoc.x}, ${minMax.maxLoc.y})`, minMax);
    const maxLoc = minMax.maxLoc;
    const maxVal = minMax.maxVal
    result.delete();
    return { maxVal,...maxLoc };
}

// 主处理流程
 function handleImage(sceneImg, tmplImg) {
    //const sceneImg = await loadImage(img1);
    const sceneMat = cv.imread(sceneImg);

    //const tmplImg = await loadImage(img2);
    let templateMat = cv.imread(tmplImg);
    // 预处理流水线
    const sceneEdges = preprocessImage(sceneMat);
    templateEdges = preprocessImage(templateMat);
    // 执行匹配
     const { x, y, maxVal } = matchTemplate(sceneEdges, templateEdges);

    // 绘制结果
   // drawRst(sceneMat, x, y, templateMat)
     let rst = { maxVal,x, y,  w: templateMat.cols, h: templateMat.rows }
     console.log(rst,"查找结果包装")
     // 内存清理
     try {
        [sceneMat.delete(), sceneEdges.delete()];  //.forEach(m => m.delete());
     } catch (e) {
         console.log("清理cv对象异常",e)
     }
     
     return rst;
}

function drawRst(sceneMat, x, y, templateMat) {
    const canvas = document.getElementById('outputCanvas')
    if (!canvas)
        return
    cv.imshow(canvas, sceneMat)

    const ctx = canvas.getContext('2d')
    ctx.strokeStyle = '#00FF00'
    ctx.lineWidth = 2
    ctx.strokeRect(x, y, templateMat.cols, templateMat.rows)
}

// 图像加载工具函数
function loadImage(file) {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = e => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.src = e.target.result;
        };
        reader.readAsDataURL(file);
    });
}

function loadImageBy64(base64Str) {
    return new Promise((resolve) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.src = base64Str
    });
}
const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
function executeAndWait(func, delay,...args) {
    return new Promise((resolve, reject) => {
        try {
            const result = func();
            setTimeout(() => resolve(result), delay,...args);
        } catch (error) {
            reject(error);
        }
    });
}
/**
 * 异步重试函数
 * @param {Function} task - 返回 Promise 的异步任务
 * @param {number} maxRetries - 最大重试次数（默认3次）
 * @param {number} delay - 重试间隔（默认1秒）
 * @param {(error) => boolean} [shouldRetry] - 判断是否重试的函数
 * @returns {Promise} - 最终结果或错误
 */
function retry(task, maxRetries = 3, delay = 1000, shouldRetry = () => true) {
    return new Promise(async (resolve, reject) => {
        let attempt = 0;

        const execute = async () => {
            try {
                // 将同步函数包装为 Promise
                const result = await Promise.resolve().then(task);
                resolve(result);
            } catch (err) {
                attempt++;
                // 判断是否满足重试条件
                if (attempt <= maxRetries && shouldRetry(err)) {
                    console.log(`第 ${attempt} 次重试，等待 ${delay}ms`);
                    if (delay < 0)
                        execute()
                    else
                        setTimeout(execute, delay);
                } else {
                    reject(err);
                }
            }
        }
        execute();
    })
  }
